/**
 * @file lv_gpu_v2d.h
 *
 */

#ifndef LV_GPU_V2D_H
#define LV_GPU_V2D_H

#include <stdio.h>

#include "../lv_draw/lv_draw_blend.h"
/* support to include lv_conf.h */
#include "../lv_misc/lv_types.h"

#ifdef __cplusplus
extern "C" {
#endif

/* only pc simulator lv_conf.h has defined, others temperally use 0 */
#ifndef LV_USE_GPU_V2D
#define LV_USE_GPU_V2D          0
#endif

#ifndef LV_USE_GPU_V2D_SOFTWARE_SIMULATOR
#define LV_USE_GPU_V2D_SOFTWARE_SIMULATOR  0
#endif

/*********************
 *      INCLUDES
 *********************/
#include "../lv_misc/lv_area.h"
#include "../lv_misc/lv_color.h"
#include "../lv_hal/lv_hal_disp.h"

/*********************
 *      DEFINES
 *********************/


/**********************
 *      TYPEDEFS
 **********************/
/* RB can swap by v2d register */
typedef enum {
    RGB888,   /* lv_color_rgb888_t */
    RGBX8888,
    RGBA8888, /* lv_color_rgba8888_t; if BGRA8888, need set RB swap of v2d, BGRA8888->lv_color32_t */
    ARGB8888,
    RGB565,    /* if BGR565,  need set RB swap of v2d, BGA565->lv_color16_t, LV_COLOR_16_SWAP=0 */
    RGBA5658,  /* if BGAR5658, need set RB swap of v2d, BGA5658->lv_color16_alpha_t*/
    ARGB8565,
    A8,        /* 8bit alpha image, used for mask */
    Y8,        /* 8bit gray image, if need black and white, software transfer gray to Black and white */
    YUV420,     /* semi planar (UV can swap) */
    FORMAT_DEFAULT
} LV_V2D_IMAGE_FORMAT;

typedef enum {
    MIX_TYPE_0, /* one */
    MIX_TYPE_1, /* zero */
    MIX_TYPE_2, /* source_alpha, it can be from pixel/regiseter/mask buffer */
    MIX_TYPE_3, /* 1-source_alpha */
    MIX_TYPE_4, /* dst_alpha, from bg */
    MIX_TYPE_5, /* 1-dst_alpha */
} LV_V2D_BLEND_MIX_TYPE;

#define LV_MIX_FROM_SOURCE_ALPHA MIX_TYPE_2

typedef enum {
    ALPHA_FROM_PIXEL, /* from alpha channel of bg image's pixel */
    ALPHA_FROM_REGISTER, /* can be overall opa */
    ALPHA_FROM_MASK_BUFFER, /* source_alpha, from fg */
} LV_V2D_BLEND_ALPHA_SELECTION;

/*
 * Support alpha preprocess, a v2d register for factor for premix,
 * eg: if the premix factor is overall opa, alpha selection is ALPHA_FROM_MASK_BUFFER,
 * then source_alpha = (premix factor * px_opa from mask) >> 8;
 *
 * bg_B = fg_B * source_alpha + bg_B *(1 - source_alpha);
 * bg_G = fg_G * source_alpha + bg_G *(1 - source_alpha);
 * bg_R = fg_R * source_alpha + bg_R *(1 - source_alpha);
 * bg_A = 255 or no bg_A; which will not transfer to LCD(BGA565)
 */

/* crop or blend relative position for bg or fg area */
typedef struct {
    uint16_t x; /* left */
    uint16_t y; /* top */
    uint16_t w; /* crop width */
    uint16_t h; /* crop height */
} V2D_AREA;

typedef struct
{
    void *buf;
    uint32_t pitch;
    uint32_t w;
    uint32_t h;
    uint32_t format;
    V2D_AREA crop_area;
    bool rb_swap;
} V2D_SRC_SURFACE;

typedef struct
{
    void *buf; /* blend output buf, if bg format same as output format, output buf can be same as bg_buf, else, they must be different */
    uint32_t format;
    V2D_AREA blend_area;
    bool rb_swap;
    uint8_t dither; /* BGRA 8888 ot BGR 565 need */
    LV_V2D_BLEND_MIX_TYPE mix_type;
    LV_V2D_BLEND_ALPHA_SELECTION alpha_selection;
    uint8_t premult_factor; /* fg's overall_opa */
    uint8_t premult_enable;
} V2D_BLEND_OUT_SURFACE;

typedef struct
{
    V2D_SRC_SURFACE bg_surface;
    V2D_SRC_SURFACE fg_surface;
    V2D_SRC_SURFACE mask_surface;
    V2D_BLEND_OUT_SURFACE out_surface;
} V2D_BLEND_T;

typedef struct
{
    V2D_SRC_SURFACE src_surface;
    V2D_SRC_SURFACE dst_surface;
} V2D_TRANSFER_T;

/**********************
 * GLOBAL PROTOTYPES
 **********************/

/**********************
 *      MACROS
 **********************/
int lv_gpu_v2d_blend(const lv_area_t * bg_area, uint8_t * bg_buf, LV_V2D_IMAGE_FORMAT bg_format,
                     const lv_area_t * blend_area,
                     const lv_area_t * fg_area, const uint8_t * fg_buf, lv_opa_t opa, LV_V2D_IMAGE_FORMAT fg_format,
                     const lv_area_t * mask_area, const lv_opa_t * mask_buf,
                     lv_draw_mask_res_t mask_res,
                     lv_blend_mode_t blend_mode);

int lv_gpu_A8_blend(const lv_area_t * bg_area, uint8_t * bg_buf,
                    const lv_area_t * blend_area,
                    const lv_area_t * fg_area, const uint8_t * fg_buf);

/* crop also supports transfer format */
int lv_gpu_v2d_crop_and_transfer(const lv_area_t * src_area, const uint8_t * src_buf, LV_V2D_IMAGE_FORMAT src_format,
                                 const lv_area_t * dst_area, uint8_t * dst_buf, LV_V2D_IMAGE_FORMAT dst_format);

/* src_buf has alpha channel, eg BGRA5658, divide alpha to mask_buf, reverse BGR to dst_buf */
int lv_gpu_v2d_divide_alpha_to_mask_A8(const lv_area_t * src_area, const uint8_t * src_buf,
                                       const lv_area_t * dst_area, uint8_t * dst_buf,
                                       lv_opa_t * mask_buf);

/* src and dst with same default format */
int lv_gpu_v2d_crop(const lv_area_t * src_area, const uint8_t * src_buf,
                    const lv_area_t * dst_area, uint8_t * dst_buf);

int lv_gpu_v2d_transfer(const lv_area_t * src_area, const uint8_t * src_buf, LV_V2D_IMAGE_FORMAT src_format,
                        lv_color_t * dst_buf, LV_V2D_IMAGE_FORMAT dst_format);

int lv_gpu_fill_color(const lv_area_t * bg_area, uint8_t * bg_buf,
                      lv_color_t fg_recolor, lv_opa_t fg_recolor_opa,
                      const lv_area_t * fg_fill_area,
                      uint8_t * mask_buf, lv_draw_mask_res_t mask_res,
                      lv_blend_mode_t blend_mode);

/* lv draw mask always line by line, there prepare a whole map of mask to blend */
int lv_gpu_all_masks_blend( const lv_area_t * blend_area, uint8_t *mask_buf, lv_draw_mask_res_t *mask_res);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /*LV_GPU_V2D_H*/
